﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Asol.Reporting.Support
{
    public partial class Assistant
    {
        #region Pluginy - načtení (reflexe odpovídajících assembly)
        /// <summary>
        /// Inicializace systému Pluginů
        /// </summary>
        [InitializeMethod(-32000)]            // Načítání pluginů nepoužívá nic jiného než adresář aplikace, ten se inicializuje o něco dříve
        protected void InitPlugin()
        {
            this._IsProgrammer = System.Diagnostics.Debugger.IsAttached;
            lock (this._PluginMapLock)
            {
                this._PluginMap = new Dictionary<Type, Dictionary<Type, IPlugin>>();
                this._LoadAssemblies();
                List<Type> serviceTypes = this._GetInterfaceList();
                this._MapPlugins(serviceTypes);
            }
        }
        /// <summary>
        /// Metoda najde všechny interface, které jsou potomkem IPlugin, a vrátí jejich seznam.
        /// Hledá v dodaných assembly.
        /// </summary>
        /// <returns></returns>
        private List<Type> _GetInterfaceList()
        {
            Type iPlugin = typeof(IPlugin);
            List<Type> pluginTypes = new List<Type>();
            foreach (Assembly assembly in this._AssemblyList)
            {
                string file = assembly.Location;
                try
                {
                    foreach (Type interfaceType in assembly.GetTypes())
                    {
                        if (!interfaceType.IsInterface) continue;

                        // Najdu všechny interface (do foundTypes), které daný datový typ implementuje:
                        Type[] foundTypes = interfaceType.FindInterfaces(delegate(Type t, object data) { return (t == (Type)data); }, iPlugin); // Anonymní delegát dostává postupně všechny interface, které datový typ implementuje; má vrátit true, pokud máme konkrétní interface zařadit do výstupu:
                        if (foundTypes.Length > 0)
                            pluginTypes.Add(interfaceType);
                    }
                }
                catch (System.Reflection.ReflectionTypeLoadException ex1)
                {
                    string msg = "";
                    foreach (Exception lex in ex1.LoaderExceptions)
                        msg += lex.Message + Environment.NewLine;
                    msg = @"V adresáři aplikace jsou chybně přeložené soubory." + Environment.NewLine + "Soubor: " + file + " obsahuje chyby:" + Environment.NewLine + Environment.NewLine + msg;
                }
                catch (Exception ex2)
                {
                    string msg = @"V adresáři aplikace jsou uloženy soubory, které nejsou kompatibilní.
Při načítání programového souboru: " + file + @"
došlo k chybě: " + ex2.Message;
                }
            }

            return pluginTypes;
        }
        /// <summary>
        /// Pomocí reflexe doplní existující seznam this._PluginMap o další služby a jejich poskytovatele.
        /// Zajistí načtení (reflexí fyzických knihoven) těch služeb, které jsou požadované, a přitom dosud nejsou načtené (ty už nedohledává).
        /// </summary>
        private void _MapPlugins(IEnumerable<Type> findServiceTypes)
        {
            // Základní kostra mapy pluginů: klíč = typ interface pluginu:
            foreach (Type service in findServiceTypes)
            {
                if (!this._PluginMap.ContainsKey(service))
                    this._PluginMap.Add(service, new Dictionary<Type, IPlugin>());
            }

            // Následující smyčka a rychlost:
            // Načtení mapy typů ze dvou knihoven (Schedule a Support) s celkovým počtem typů = 500 trvá 50 milisekund.
            foreach (Assembly assembly in this._AssemblyList)
            {
                string file = assembly.Location;
                try
                {
                    foreach (Type pluginType in assembly.GetTypes())
                    {
                        // Přebíráme pouze class, které nejsou abstract:
                        if (!pluginType.IsClass) continue;
                        if (pluginType.IsAbstract) continue;

                        // Najdu všechny zajímavé interface (ty, které jsou uvedeny v this._PluginMap) které daný datový typ implementuje, uložím je do foundTypes:
                        Type[] foundTypes = pluginType.FindInterfaces(delegate(Type t, object data) { return this._PluginMap.ContainsKey(t); }, null);	// Anonymní delegát dostává postupně všechny interface, které datový typ implementuje. Má vrátit true, pokud máme konkrétní interface zařadit do výstupu:
                        if (foundTypes.Length > 0)
                        {
                            // Vytvořím si new instanci konkrétního pluginu, ověřím její použitelnost v daném prostředí a případně ji ignoruji:
                            IPlugin pluginInstance = this._CreatePlugin(pluginType);
                            if (pluginInstance != null)
                            {
                                // V poli foundTypes mám typy = interface, které implementuje datový typ, a na které jsme se ptali.
                                // Jedna třída může implementovat více pluginů, pak bude její reference uložena do více řádků mapy:
                                foreach (Type implementedInterface in foundTypes)
                                    this._PluginMap[implementedInterface].Add(pluginType, pluginInstance);
                            }
                        }
                    }
                }
                catch (System.Reflection.ReflectionTypeLoadException ex1)
                {
                    string msg = "";
                    foreach (Exception lex in ex1.LoaderExceptions)
                        msg += lex.Message + Environment.NewLine;
                    msg = @"V adresáři aplikace jsou chybně přeložené soubory." + Environment.NewLine + "Soubor: " + file + " obsahuje chyby:" + Environment.NewLine + Environment.NewLine + msg;
                }
                catch (Exception ex2)
                {
                    string msg = @"V adresáři aplikace jsou uloženy soubory, které nejsou kompatibilní.
Při načítání programového souboru: " + file + @"
došlo k chybě: " + ex2.Message;
                }
            }
        }
        /// <summary>
        /// Vytvoří a vrátí objekt daného typu, typovaný jako IPlugin, nebo vrátí null.
        /// </summary>
        /// <param name="pluginType"></param>
        /// <returns></returns>
        private IPlugin _CreatePlugin(Type pluginType)
        {
            if (pluginType == null) return null;
            object target = System.Activator.CreateInstance(pluginType);
            IPlugin plugin = target as IPlugin;
            if (plugin == null) return null;
            bool isDebug = this._IsProgrammer;
            switch (plugin.PluginActivity)
            {
                case PluginActivityMode.None: return null;
                case PluginActivityMode.OnlyDebugMode: return (isDebug ? plugin : null);
                case PluginActivityMode.OnlyRunMode: return (!isDebug ? plugin : null);
                case PluginActivityMode.Standard: return plugin;
            }
            return plugin;
        }
        /// <summary>
        /// Mapa obsahující tolik položek, kolik najdeme interface, které jsou potomkem IPlugin.
        /// Klíčem v mapě (Key) je Type každého interface, který je potomkem IPlugin.
        /// V jednotlivé položce mapy (Value) je seznam typů konkrétních tříd (class, non abstract) (jako vnořená Dictionary), které daný interface (konkrétní službu) implementují.
        /// U konkrétního typu (který je klíčem ve vnořené Dictionary) je uložena i prvotní instance daného typu.
        /// Mapu lze tedy chápat jako index: [Type interface, [Type implementace, objekt implementace]].
        /// </summary>
        private Dictionary<Type, Dictionary<Type, IPlugin>> _PluginMap;
        /// <summary>
        /// Zámek na tvorbu mapy pluginů
        /// </summary>
        private object _PluginMapLock = new object();
        #endregion
        #region Pluginy - public metody pro jejich získání
        /// <summary>
        /// Vrátí soupis TYPŮ poskytovatelů služeb daného druhu.
        /// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
        /// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
        /// Výstupem je soupis typů konkrétních poskytovatelů této služby.
        /// </summary>
        /// <param name="service">Typ služby, například typeof(IGraphElementPainter)</param>
        /// <returns></returns>
        public static IEnumerable<Type> GetServiceTypeList(Type service)
        {
            return Live._GetServiceTypeList(service);
        }
        /// <summary>
        /// Vrátí soupis SKUTEČNÝCH OBJEKTŮ poskytovatelů služeb daného druhu.
        /// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
        /// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
        /// Výstupem je soupis objektů konkrétních poskytovatelů této služby.
        /// </summary>
        /// <typeparam name="S">Typ služby, například IGraphElementPainter</typeparam>
        /// <param name="cached">Příznak, zda je možno číst objekty z cache</param>
        /// <returns>Soupis objektů</returns>
        public static IEnumerable<S> GetServiceObjectList<S>(bool cached) where S : IPlugin
        {
            return Live._GetServiceObjectList<S>(cached);
        }
        /// <summary>
        /// Vrátí objekt jednoho konkrétního poskytovatele služby daného druhu.
        /// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
        /// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
        /// Výstupem je soupis objektů konkrétních poskytovatelů této služby.
        /// </summary>
        /// <typeparam name="S">Typ služby, například IGraphElementPainter</typeparam>
        /// <param name="providerType">Typ konkrétního poskytovatele služby, například typeof(PainterGanttRectangleCls), či spíš typ načtený z metody GetServiceTypeList()</param>
        /// <param name="cached">Příznak, zda je možno číst objekt z cache</param>
        /// <returns>Konkrétní poskytovatel anebo null</returns>
        public static S GetServiceObject<S>(Type providerType, bool cached) where S : IPlugin
        {
            return Live._GetServiceObject<S>(providerType, cached);
        }
        /// <summary>
        /// Najde a vrátí soupis TYPŮ poskytovatelů dané služby.
        /// Pokud požadovaná služba není v interním seznamu (nebyla přítomna při inicializaci),
        /// dohledá její poskytovatele nyní. Ztráta je jen časová. Příště už budou načteny i data pro tuto novou službu.
        /// </summary>
        /// <param name="service"></param>
        /// <returns></returns>
        private IEnumerable<Type> _GetServiceTypeList(Type service)
        {
            Dictionary<Type, IPlugin> providers = _GetServiceProviders(service);
            if (providers == null) return null;
            return new List<Type>(providers.Keys);
        }
        /// <summary>
        /// Najde a vrátí soupis INSTANCÍ poskytovatelů dané služby.
        /// Vrací soupis objektů, které jsou vytvořeny z typu konkrétního poskytovatele.
        /// Pokud požadovaná služba není v interním seznamu (nebyla přítomna při inicializaci),
        /// dohledá její poskytovatele nyní. Ztráta je jen časová. Příště už budou načteny i data pro tuto novou službu.
        /// </summary>
        /// <typeparam name="S">Typ služby = typ interface, který má služba implementovat. Vrací objekty typované tímto typem.</typeparam>
        /// <param name="cached">true = umožní číst objekty z cache / false = chce vždy čerstvé objekty. Pozor, čas!!!</param>
        /// <returns></returns>
        private IEnumerable<S> _GetServiceObjectList<S>(bool cached) where S : IPlugin
        {
            Type service = typeof(S);                      // Typ služby
            List<S> result = new List<S>();                // Výstup

            Dictionary<Type, IPlugin> providers = this._GetServiceProviders(service);
            if (providers == null) return null;
            IEnumerable<Type> types = new List<Type>(providers.Keys);
            foreach (Type type in types)
            {
                if (cached)
                {	// Objekty z cache (anebo nové do cache a pak z cache ven):
                    // Poznámka: v cache (providers) vždy existuje položka pro klíč (type), 
                    //     protože seznam typů (types) jsme načetli z tohoto soupisu. 
                    //     Důvod: Dictionary nelze procházet cyklem foreach() a přitom do ní vepisovat nová data.
                    if (providers[type] == null)
                        providers[type] = ObjectGenerator<S>(type);
                    result.Add((S)providers[type]);
                }
                else
                {	// Objekty do výstupu vždy nové:
                    result.Add(ObjectGenerator<S>(type));
                }
            }
            return result;
        }
        /// <summary>
        /// Vrátí objekt jednoho konkrétního poskytovatele služby daného druhu.
        /// Vstupem je typ služby = určení interface, který má služba (servisní třída) nabízet.
        /// Tento typ služby musí být jedním z těch, které byly předány do inicializace (v parametru metody Steward.AppInit().
        /// Výstupem je soupis objektů konkrétních poskytovatelů této služby.
        /// </summary>
        /// <typeparam name="S">Typ služby, například IGraphElementPainter</typeparam>
        /// <param name="providerType">Typ konkrétního poskytovatele služby, například typeof(PainterGanttRectangleCls), či spíš typ načtený z metody GetServiceTypeList()</param>
        /// <param name="cached">Příznak, zda je možno číst objekt z cache</param>
        /// <returns>Konkrétní poskytovatel anebo null</returns>
        private S _GetServiceObject<S>(Type providerType, bool cached) where S : IPlugin
        {
            Type service = typeof(S);                      // Typ služby

            // Najdu seznam služeb požadovaného typu S: (jde o soupis typů a cachovaných objektů providerů):
            Dictionary<Type, IPlugin> providers = _GetServiceProviders(service);
            if (providers == null) return default(S);

            // Najdu konkrétního providera, pokud nebude v seznamu tak ho ani nebudu vytvářet, nejspíš jde o jiný typ než je přípustné:
            IPlugin provider = null;
            if (!providers.TryGetValue(providerType, out provider)) return default(S);

            // Z cache / do cache anebo nový objekt:
            if (cached)
            {	// Objekty z cache (anebo nové do cache a pak z cache ven):
                // Poznámka: v cache (providers) vždy existuje položka pro klíč (type), 
                //     protože seznam typů (types) jsme načetli z tohoto soupisu. 
                //     Důvod: Dictionary nelze procházet cyklem foreach() a přitom do ní vepisovat nová data.
                if (provider == null)
                {
                    provider = ObjectGenerator<S>(providerType);
                    providers[providerType] = provider;
                }
            }
            else
            {	// Objekty do výstupu vždy nové:
                provider = ObjectGenerator<S>(providerType);
            }
            return (S)provider;
        }
        /// <summary>
        /// Metoda vrátí vnitřní dictionary pro danou službu.
        /// Pokud služba není načtena, načte ji.
        /// Vrací živou referenci do interní paměti.
        /// </summary>
        /// <param name="service"></param>
        /// <returns></returns>
        private Dictionary<Type, IPlugin> _GetServiceProviders(Type service)
        {
            Dictionary<Type, IPlugin> providers;
            // Máme už načtené providery dané služby:
            if (this._PluginMap.TryGetValue(service, out providers)) return providers;

            // Nemáme => můžeme služby zamknout, zkusit je donačíst, atd:
            lock (this._PluginMapLock)
            {
                if (this._PluginMap.TryGetValue(service, out providers)) return providers;

                List<Type> serviceTypes = new List<Type>();
                serviceTypes.Add( service);
                this._MapPlugins(serviceTypes);

                if (this._PluginMap.TryGetValue(service, out providers)) return providers;
            }

            return null;
        }
        /// <summary>
        /// Vrátí nově vytvořený objekt daného typu.
        /// Pokud se nepodaří, vrátí null.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <returns></returns>
        public static T ObjectGenerator<T>(Type type)
        {
            return ObjectGenerator<T>(type, true);
        }
        /// <summary>
        /// Vrátí nově vytvořený objekt daného typu.
        /// Pokud se nepodaří, vrátí null.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="suppressException">Potlačit výjimku</param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static T ObjectGenerator<T>(Type type, bool suppressException)
        {
            T result = default(T);
            try
            {
                object obj = System.Activator.CreateInstance(type, false);
                result = (T)obj;
            }
            catch (Exception)
            {
                if (suppressException)
                    result = default(T);
                else
                    throw;
            }
            return result;
        }
        #endregion
        #region TypeToEditStyle
        /// <summary>
        /// Inicializace systému Pluginů
        /// </summary>
        [InitializeMethod(-31000)]            // Jde jen o iniciaci
        protected void InitTypeEditStyles()
        {
            this._TypeStyleItemList = new Dictionary<Type, List<FormatStyleItem>>();
        }
        /// <summary>
        /// Z daného typu extrahuje editační styl.
        /// Typ je typicky Enum, může to být i třída nebo struktura deklarující konstanty.
        /// Z takového podkladu (prvky enumu, konstanty) tato metoda vytvoří seznam položek editačího stylu a vrátí jej.
        /// U každé property vyhledá Custom atribut PropertyName
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<FormatStyleItem> GetEditStyleItemsFromType(Type type)
        {
            return Live._GetEditStyleItemsFromType(type);
        }
        /// <summary>
        /// Získá editační styl z cache nebo jej načte z daného typu
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private IEnumerable<FormatStyleItem> _GetEditStyleItemsFromType(Type type)
        {
            // type = typeof(Sample);
            List<FormatStyleItem> items;
            if (this._TypeStyleItemList.TryGetValue(type, out items)) return items;
            if (type.IsEnum)
                items = _GetEditStyleItemsFromEnum(type);
            else
                items = _GetEditStyleItemsFromOther(type);

            lock (this._TypeStyleItemList)
            {
                if (!this._TypeStyleItemList.ContainsKey(type))
                    this._TypeStyleItemList.Add(type, items);
            }
            return items;
        }
        /// <summary>
        /// Získá editační styl z enumu
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private List<FormatStyleItem> _GetEditStyleItemsFromEnum(Type type)
        {
            List<FormatStyleItem> items = new List<FormatStyleItem>();
            Array values = type.GetEnumValues();
            string[] names = type.GetEnumNames();
            FieldInfo[] fields = type.GetFields();
            int cnt = (values.Length < names.Length ? values.Length : names.Length);
            for (int i = 0; i < cnt; i++)
            {
                string text = names[i];
                string info = null;
                FieldInfo field = fields.FirstOrDefault(f => f.Name == text);
                if (field != null)
                {
                    object[] atts = field.GetCustomAttributes(typeof(DisplayTextInfoAttribute), true);
                    DisplayTextInfoAttribute dti = atts.FirstOrDefault() as DisplayTextInfoAttribute;
                    if (dti != null)
                    {
                        text = dti.Text;
                        info = dti.Info;
                    }
                }
                items.Add(new FormatStyleItem(values.GetValue(i), text, info));
            }
            return items;
        }
        /// <summary>
        /// Získá editační styl z třídy nebo struktury, z jejich konstant
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private List<FormatStyleItem> _GetEditStyleItemsFromOther(Type type)
        {
            List<FormatStyleItem> items = new List<FormatStyleItem>();

            FieldInfo[] fields = type.GetFields();
            foreach (FieldInfo field in fields)
            {
                if (!(field.IsLiteral && field.IsStatic)) continue;
                object value = field.GetValue(null);
                string text = field.Name;
                string info = null;
                object[] atts = field.GetCustomAttributes(typeof(DisplayTextInfoAttribute), true);
                DisplayTextInfoAttribute dti = atts.FirstOrDefault() as DisplayTextInfoAttribute;
                if (dti != null)
                {
                    text = dti.Text;
                    info = dti.Info;
                }
                items.Add(new FormatStyleItem(value, text, info));
            }
            return items;
        }
        /// <summary>
        /// Cache pro editační styly již zmapovaných typů
        /// </summary>
        private Dictionary<Type, List<FormatStyleItem>> _TypeStyleItemList;
        #endregion
        #region AssemblyList a AssemblyResolver
        /// <summary>
        /// Najde a vrátí Type podle jeho Fullname (typeof().FullName).
        /// Řeší převod AssemblyName na Assembly.
        /// </summary>
        /// <param name="typeName">Plný název typu (Type.FullName)</param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool TryGetType(string typeName, out Type type)
        {
            type = System.Type.GetType(typeName, _AssemblyResolver, _TypeResolver, false);
            return (type != null);
        }
        /// <summary>
        ///     A method that locates and returns the assembly that is specified in typeName.
        ///     The assembly name is passed to assemblyResolver as an System.Reflection.AssemblyName
        ///     object. If typeName does not contain the name of an assembly, assemblyResolver
        ///     is not called. If assemblyResolver is not supplied, standard assembly resolution
        ///     is performed. CautionDo not pass methods from unknown or untrusted callers.
        ///     Doing so could result in elevation of privilege for malicious code. Use only
        ///     methods that you provide or that you are familiar with.
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        private static Assembly _AssemblyResolver(AssemblyName assemblyName)
        {
            return Live._AssemblyList.FirstOrDefault(a => a.FullName == assemblyName.FullName);
        }
        /// <summary>
        ///     A method that locates and returns the type that is specified by typeName
        ///     from the assembly that is returned by assemblyResolver or by standard assembly
        ///     resolution. If no assembly is provided, the typeResolver method can provide
        ///     one. The method also takes a parameter that specifies whether to perform
        ///     a case-insensitive search; false is passed to that parameter. CautionDo not
        ///     pass methods from unknown or untrusted callers.
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="typeName">Plný název typu (Type.FullName)</param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        private static Type _TypeResolver(Assembly assembly, string typeName, bool ignoreCase)
        {
            if (assembly == null)
                assembly = Live._AssemblyList.FirstOrDefault(a => a.GetTypes().Any(t => t.FullName == typeName));
            if (assembly != null)
                return assembly.GetType(typeName, false, ignoreCase);
            return null;
        }
        /// <summary>
        /// Načte a vrátí všechny patřičné assemblies
        /// </summary>
        /// <returns></returns>
        private void _LoadAssemblies()
        {
            string path = this._AppPath;
            System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(path);
            System.IO.FileInfo[] files = dirInfo.GetFiles("*.*", System.IO.SearchOption.AllDirectories);

            List<Assembly> assemblyList = new List<Assembly>();
            assemblyList.Add(Assembly.GetExecutingAssembly());                      // Přidám sebe, jsem Support (jsem naloadován)
            System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
            string executing = currentProcess.MainModule.FileName;
            this._LoadAssemblyOne(executing, null, assemblyList);                   // Přidám main module assembly
            this._LoadAssemblies(files, n => _IsReportCodeFile(n), assemblyList);   // Přidám ostatní naše soubory

            this._AssemblyList = assemblyList;
        }
        /// <summary>
        /// Určí, zda soubor daného jména patří do zdejšího systému
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private bool _IsReportCodeFile(string fileName)
        {
            string name = System.IO.Path.GetFileNameWithoutExtension(fileName).ToLower();      // "asol.report"
            string ext = System.IO.Path.GetExtension(fileName).ToLower();                      // ".dll"
            return (name.Contains(".reporting.") && ext == ".dll");
        }
        /// <summary>
        /// Z dodaného seznamu souborů vyfiltruje potřebná jména, pro vyhovující jména je naloaduje jako assembly a přidá je do seznamu assemblyList.
        /// </summary>
        /// <param name="files"></param>
        /// <param name="filter"></param>
        /// <param name="assemblyList"></param>
        private void _LoadAssemblies(System.IO.FileInfo[] files, Func<string, bool> filter, List<Assembly> assemblyList)
        {
            foreach (System.IO.FileInfo file in files)
            {
                this._LoadAssemblyOne(file.FullName, filter, assemblyList);
            }
        }
        /// <summary>
        /// Načte jednu assembly danou jménem souboru
        /// </summary>
        /// <param name="fullName"></param>
        /// <param name="filter"></param>
        /// <param name="assemblyList"></param>
        private void _LoadAssemblyOne(string fullName, Func<string, bool> filter, List<Assembly> assemblyList)
        {
            if (assemblyList.Exists(a => String.Equals(a.Location, fullName, StringComparison.InvariantCultureIgnoreCase)))
                return;

            if (filter != null && !filter(fullName))
                return;

            string file = System.IO.Path.GetFileName(fullName);

            try
            {
                Assembly assembly = Assembly.LoadFile(fullName);
                assemblyList.Add(assembly);
            }
            catch (System.Reflection.ReflectionTypeLoadException ex1)
            {
                string msg = "";
                foreach (Exception lex in ex1.LoaderExceptions)
                    msg += lex.Message + Environment.NewLine;
                msg = @"V adresáři aplikace jsou chybně přeložené soubory." + Environment.NewLine + "Soubor: " + file + " obsahuje chyby:" + Environment.NewLine + Environment.NewLine + msg;
            }
            catch (Exception ex2)
            {
                string msg = @"V adresáři aplikace jsou uloženy soubory, které nejsou kompatibilní.
Při načítání programového souboru: " + fullName + @"
došlo k chybě: " + ex2.Message;
            }
        }
        /// <summary>
        /// Soupis assemblies, které jsou načteny
        /// </summary>
        private List<Assembly> _AssemblyList;
        #endregion
    }
    #region interface IPlugin + enum
    /// <summary>
    /// Základní deklarace pluginu: kdy se má používat a pod jakou licencí
    /// </summary>
    public interface IPlugin
    {
        /// <summary>
        /// Kdy je plugin aktivní
        /// </summary>
        PluginActivityMode PluginActivity { get; }
        /// <summary>
        /// Licenční kód, povoluje použití pluginu.
        /// </summary>
        string LicenseCode { get; }
    }
    /// <summary>
    /// V jaké situaci se má plugin používat
    /// </summary>
    public enum PluginActivityMode
    {
        /// <summary>Plugin nebude použitelný nikdy. Může být naprogramován, ale v místě použití se neobjeví.</summary>
        None = 0,
        /// <summary>Plugin je aktivní jen v programátorském běhu, když je připojen debugger</summary>
        OnlyDebugMode,
        /// <summary>Plugin je aktivní jen v uživatelském běhu, když není připojen debugger</summary>
        OnlyRunMode,
        /// <summary>Plugin je aktivní vždy.</summary>
        Standard
    }
    #endregion
}
